home *** CD-ROM | disk | FTP | other *** search
- (* -------------------------------------------------------------- *)
- (* FileSpec.PAS v1.0a by Robert Walking-Owl November 1993 *)
- (* -------------------------------------------------------------- *)
-
- { Things to add... }
- { - have # and $ be symbols for ASCII chars in dec/hex? }
-
- (* Buggie Things: *)
- (* - anti-sets don't work with variable lenght sets, since they *)
- (* end with the first character NOT in the set... *)
-
- {$F+}
-
- unit FileSpec;
-
- interface
-
- uses Dos;
-
- const
- DosNameLen = 12; (* Maximum Length of DOS filenames *)
- UnixNameLen = 32; (* Maximum Length of Unix Filenames *)
-
- MaxWildArgs = 32; (* Maximum number of wildcard arguments *)
- MaxNameLen = 127;
-
- fCaseSensitive = $01; (* Case Sensitive Flag *)
- fExtendedWilds = $02; (* Use extented wildcard forms (not,sets *)
- fUndocumented = $80; (* Use DOS 'undocumented' filespecs *)
-
- type
- SpecList = array [1..MaxWildArgs] of record
- Name: string[ MaxNameLen ]; (* or use DOS ParamStr? *)
- Truth: Boolean
- end;
- PWildCard = ^TWildCard;
- TWildCard = object
- private
- FileSpecs: SpecList; (* List of filespecs *)
- NumNegs, (* Number of "not" specs *)
- FSpCount: word; (* Total number of specs *)
- function StripQuotes( x: string ): string;
- procedure FileSplit(Path: string;
- var Dir,Name,Ext: string);
- public
- PathChar, (* path seperation char *)
- NotChar, (* "not" char - init '~' *)
- QuoteChar: Char; (* quote char - init '"' *)
- Flags, (* Mode flags ... *)
- FileNameLen: Byte; (* MaxLength of FileNames *)
- constructor Init;
- procedure AddSpec( name: string);
- function FitSpec( name: string): Boolean;
- destructor Done;
- (* Methods to RemoveSpec() or ChangeSpec() aren't added *)
- (* since for most applications they seem unnecessary. *)
- (* An IsValid() spec to see if a specification is valid *)
- (* syntax is also unnecessary, since no harm is done, *)
- (* and DOS and Unix ignore them anyway .... *)
- end;
-
-
- implementation
-
- procedure UpCaseStr( var S: string); assembler;
- asm
- PUSH DS
- LDS SI,S
- MOV AL,BYTE PTR DS:[SI]
- XOR CX,CX
- MOV CL,AL
- @STRINGLOOP: INC SI
- MOV AL,BYTE PTR DS:[SI]
- CMP AL,'a'
- JB @NOTLOCASE
- CMP AL,'z'
- JA @NOTLOCASE
- SUB AL,32
- MOV BYTE PTR DS:[SI],AL
- @NOTLOCASE: LOOP @STRINGLOOP
- POP DS
- end;
-
-
- constructor TWildCard.Init;
- begin
- FSpCount := 0;
- NumNegs := 0;
- NotChar := '~';
- QuoteChar := '"';
- Flags := fExtendedWilds or fUndocumented;
- FileNameLen := DosNameLen;
- PathChar := '\';
- end;
-
- destructor TWildCard.Done;
- begin
- FSpCount := 0
- end;
-
- function TWildCard.StripQuotes( x: string ): string;
- begin
- if x<>''
- then if (x[1]=QuoteChar) and (x[length(x)]=QuoteChar)
- then StripQuotes := Copy(x,2,Length(x)-2)
- else StripQuotes := x
- end;
-
- procedure TWildCard.AddSpec( Name: string);
- var
- Truth: Boolean;
- begin
- if Name <> '' then begin
- Truth := True;
- if (Flags and fExtendedWilds)<>0
- then begin
- if Name[1]=NotChar
- then begin
- inc(NumNegs);
- Truth := False;
- Name := Copy( Name , 2, Pred(Length(Name)) );
- end;
- Name := StripQuotes( Name );
- end;
- if (FSpCount<>MaxWildArgs) and (Name<>'')
- then begin
- inc( FSpCount );
- FileSpecs[ FSpCount ].Name := Name;
- FileSpecs[ FSpCount ].Truth := Truth
- end;
- end
- end;
-
- procedure TWildCard.FileSplit(Path: string; var Dir,Name,Ext: string);
- var
- i,p,e: byte;
- InSet: Boolean;
- begin
- p:=0;
- if (Flags and fCaseSensitive)=0
- then UpCaseStr(Path);
- for i:=1 to length(Path) do if Path[i]=PathChar then p:=i;
- i:=Length(Path);
- InSet := False;
- e := succ(length(Path));
- repeat
- if not Inset
- then case Path[i] of
- '.': e := i;
- ']',
- '}',
- ')': InSet := True;
- end
- else if Path[i] in ['[','{','('] then InSet := False;
- dec(i);
- until i=0;
- if p=0
- then Dir := ''
- else Dir := Copy(Path,1,p);
- Name := Copy(Path,Succ(p),pred(e-p));
- if e<=length(Path)
- then Ext := Copy(Path,e,succ(Length(Path)-e))
- else Ext := '';
- end;
-
- function TWildCard.FitSpec( name: string): Boolean;
-
- procedure Puff(var x: string); (* Pad filename with spaces *)
- begin
- while length(x)<FileNameLen do x:=x+' ';
- end;
-
-
- var x,b: set of char;
- procedure GetSet(s: string; EndSet: char; var k: byte);
- var
- c: char;
- u: string;
- i: byte;
- A: Boolean;
- begin
- A := False;
- if s[k]=',' then repeat
- inc(k)
- until (k>=FileNameLen) or (s[k]=EndSet) or (s[k]<>',');
- u := '';
- if (k<FileNameLen) and (s[k]<>EndSet) then begin
- repeat
- u := u + s[k];
- inc(k);
- until (k>=FileNameLen) or (s[k]=EndSet) or (s[k]=',');
- if u<>'' then begin
- if u[1]=NotChar
- then begin
- A := True;
- u := Copy(u,2,pred(length(u)));
- end;
- u := StripQuotes(u);
- if (length(u)=3) and (u[2]='-')
- then begin
- for c := u[1] to u[3]
- do if A then b := b+[ c ]
- else x := x+[ c ]
- end
- else begin
- for i:=1 to length(u)
- do if A then b := b+[ u[i] ]
- else x:=x+[ u[i] ];
- end
- end;
- end;
- end;
-
- function Match(n,s: string): Boolean; (* Does a field match? *)
- var i,j,k: byte;
- c: char;
- T: Boolean;
- Scrap: string;
- begin
- i := 1; (* index of filespec *)
- j := 1; (* index of name *)
- T := True;
- Puff(n);
- Puff(s);
- repeat
- if s[i]='*' then i:=FileNameLen (* Abort *)
- else
- case s[i] of
- '(' : if ((Flags and fExtendedWilds)<>0) then begin
- Scrap := '';
- inc(i);
- repeat
- Scrap := Scrap + s[i];
- inc(i);
- until (i>=FileNameLen) or (s[i]=')');
- Scrap := StripQuotes(Scrap);
- if Pos(Scrap,Copy(n,j,Length(n)))=0
- then T := False;
- end;
- '[' : if ((Flags and fExtendedWilds)<>0) then begin
- x := []; b := [];
- k:=succ(i);
- repeat
- GetSet(s,']',k);
- until (k>=FileNameLen) or (s[k]=']');
- i := k;
- if x=[] then FillChar(x,SizeOf(x),#255);
- x := x-b;
- if not (n[j] in x) then T := False;
- end;
- '{' : if ((Flags and fExtendedWilds)<>0) then begin
- x := []; b := [];
- k:=succ(i);
- repeat
- GetSet(s,'}',k);
- until (k>=FileNameLen) or (s[k]='}');
- i := succ(k);
- if x=[] then FillChar(x,SizeOf(x),#255);
- x := x-b;
- while (n[j] in x) and (j<=FileNameLen)
- do inc(j);
- end;
- else if T and (s[i]<>'?')
- then if s[i]<>n[j] then T := False;
- end;
- inc(i);
- inc(j);
- until (not T) or (s[i]='*') or (i>FileNameLen) or (j>FileNameLen);
- Match := T;
- end;
-
- var i,
- NumMatches : byte;
- dn,de,nn,ne,sn,se: string;
- Negate : Boolean;
- begin
- Negate := False;
- if FSpCount=0 then NumMatches := 1
- else begin
- NumMatches := 0;
- for i:=1 to FSpCount
- do begin
- FileSplit(name,dn,nn,ne);
- FileSplit(FileSpecs[i].Name,de,sn,se);
- if ne='' then ne:='. ';
- if (Flags and fUnDocumented)<>0 then begin
- if sn='' then sn:='*';
- if se='' then se:='.*';
- if dn='' then dn:='*';
- if de='' then de:='*';
- end;
- if (Match(dn,de) and Match(nn,sn) and Match(ne,se))
- then begin
- inc(NumMatches);
- if not FileSpecs[i].Truth
- then Negate := True;
- end;
- end;
- end;
- if (NumNegs=FSpCount) and (NumMatches=0)
- then FitSpec := True
- else FitSpec := (NumMatches<>0) xor Negate;
- end;
-
-
- end.
-
- {--------------------- DEMO ------------------------- }
-
- (* Demo program to "test" the FileSpec unit *)
- (* Checks to see if file matches filespec... good for testing/debugging *)
- (* the FileSpec object/unit, as well as learning the syntax of FileSpec *)
-
- program FileSpec_Test(input, output);
- uses FileSpec;
- var p, (* User-entered "filespec" *)
- d: String; (* Filename to "test" *)
- FS: TWildCard; (* FileSpec Object *)
- begin
- FS.Init; (* Initialize *)
- WriteLn;
- Write('Enter filespec -> '); ReadLN(p); (* Get filespec... *)
- FS.AddSpec(p); (* ... Add Spec to list ... *)
- Write('Enter file -----> '); ReadLN(d); (* ... Get Filename ... *)
- if FS.FitSpec(d) (* Is the file in the list? *)
- then WriteLN('The files match.')
- else WriteLN('The files don''t match.');
- FS.Done; (* Done... clean up etc. *)
- end.
-
-
- FileSpec v1.0a
- --------------
-
- "FileSpec" is a public domain Turbo Pascal unit that gives you advanced,
- Unix-like filespecs and wildcard-matching capabilities for your software.
- This version should be compatible with Turbo Pascal v5.5 upwards (since
- it uses OOP).
-
- The advantage is that you can check to see if a filename is within the
- specs a user has given--even multiple filespecs; thus utilities like
- file-finders or archive-viewers can have multiple file-search specif-
- ications.
-
- To use, first initialize the TWildCard object (.Init).
-
- You then use .AddSpec() to add the wildcards (e.g. user-specified) to the
- list; and use .FitSpec() to see if a filename "fits" in that list.
-
- When done, use the .Done destructor. (Check your TPascal manual if you do
- not understand how to use objects).
-
- "FileSpec" supports standard DOS wilcards (* and ?); also supported are the
- undocumented DOS wildcards (eg. FILENAME = FILENAME.* and .EXT = *.EXT).
-
- However, "FileSpec" supports many extended features which can make a program
- many times more powerful. Filenames or wildcards can be in quotes (eg. "*.*"
- is equivalent to *.*).
-
- Also supported are "not" (or "but") wildcards using the ~ character. Thus
- a hypothetical directory-lister with the argument ~*.TXT would list all
- files _except_ those that match *.TXT.
-
- Fixed and variable length "sets" are also supported:
-
- [a-m]*.* <- Any files beginning with letters A-M
- [a-z,~ux]*.* <- Any files beginning with a any letter except X or U
- *.?[~q]? <- Any files except those that match *.?Q?
- foo[abc]*.* <- Files of FOO?*.* where '?' is A,B or C
- foo["abc"]*.* <- Same as above.
- foo[a-c]*.* <- Same as above.
- test{0-9}.* <- Files of TEST0.* through TEST9999.*
- x{}z.* <- Filenames beginning with X and ending with Z
- x{0123456789}z.* <- Same as above, only with numbers between X and Z.
- ("read")*.* <- Filenames that contain the text "READ"
-
- If this seems confusing, use the FS-TEST.PAS program included with this
- archive to experiment and learn the syntax used by "FileSpec".
-
- Playing around with the included demos (LS.PAS, a directory lister; and
- XFIND, a file-finder) will also give you an idea how to use the FileSpecs
- unit.
-
- One Note: if you use the FileSpec unit with your software, please let users
- know about it in the documentation, so that they know they can take full
- advantage of the added features.
-